/*
 * Copyright 2011 SWM Services GmbH.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package de.swm.commons.mobile.client.presenter;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.HasWidgets;
import de.swm.commons.mobile.client.page.SimplePage;
import de.swm.commons.mobile.client.page.Transition;
import de.swm.commons.mobile.client.utils.AsyncCallbackSuccess;
import de.swm.commons.mobile.client.utils.Utils;
import de.swm.commons.mobile.client.widgets.page.IMainView;
import de.swm.commons.mobile.client.widgets.page.IPageWithHeader;
import de.swm.commons.mobile.client.widgets.page.IPageWithoutHeader;
import de.swm.gwt.client.eventbus.IDispatcher;
import de.swm.gwt.client.eventbus.IEvent;
import de.swm.gwt.client.mobile.Direction;
import de.swm.gwt.client.mobile.IPage;
import de.swm.gwt.client.mobile.ITransitionCompletedCallback;


/**
 * Abstract presenter class.
 */
public abstract class AbstractMobilePresenter implements IMobilePresenter {

	private final IDispatcher dispatcher;
	private boolean transitionsFromMainViewDisabled = false;

	/**
	 * The global content area is null or contains a fixed toolbar on the button of the page. *
	 */
	private static HasWidgets globalContentArea;
	private static Direction nextTransitionDirection = Direction.RIGHT;
	private static IPageWithoutHeader lastDisplayedPage = null;
	/**
	 * do not allow a transition if another one is already running. *
	 */
	private static boolean transitionIsRunning = false;
	private boolean isVisible = false;
	//private ClickHandler backClickHandler = null;


	/**
	 * Default constructor.
	 *
	 * @param disp der dispatcher.
	 */
	public AbstractMobilePresenter(IDispatcher disp) {
		this.dispatcher = disp;
	}


	@Override
	public IDispatcher getDispatcher() {
		return dispatcher;
	}

	@Override
	public LeavePageResponse leavePageRequest() {
		return LeavePageResponse.YES_LEAVE_PAGE;
	}


	/**
	 * Return value of transitionsFromMainViewDisabled (see {@link #setTransitionsFromMainViewDisabled(boolean)).
	 *
	 * @return value
	 */
	public boolean isTransitionsFromMainViewDisabled() {
		return transitionsFromMainViewDisabled;
	}

	/**
	 * If set to {code}true{code}, no transition effect is performed when page switch originates from main view (i.e., in {@link #gotoPage(IPage, IPageWithoutHeader) the
	 * originator is an instance of {@link IMainView}). I.E., if the main view is a tab panel, direct navigation via the tab header does not
	 * lead to a transition effect.
	 *
	 * @param transitionsFromMainViewDisabled
	 *         setting of transitionsFromMainViewDisabled
	 */
	public void setTransitionsFromMainViewDisabled(
			boolean transitionsFromMainViewDisabled) {
		this.transitionsFromMainViewDisabled = transitionsFromMainViewDisabled;
	}


	/**
	 * Liefert die richtung der naechsten Transition. Dieses ist normalerweise recht. Falls ein Back-Navigatiosnevent
	 * vorgemerkt ist ist es ausnahmsweise rechts.
	 *
	 * @return die Richtung des naechsten ueberganges.
	 */
	public static Direction getNextTransitionDirection() {
		if (!nextTransitionDirection.equals(Direction.RIGHT)) {
			Direction toReturn = nextTransitionDirection;
			nextTransitionDirection = Direction.RIGHT;
			return toReturn;
		}
		return nextTransitionDirection;
	}


	/**
	 * Created and adds a click handler for the back button. When the back button will be clicked the passed event will
	 * be fired (which should display the new page)
	 *
	 * @param myView      currently displayed view.
	 * @param eventToFire the event to fire after the back button will be clicked.
	 * @return the created internal click handler (e.g. to fire the event manually)
	 */
	protected ClickHandler addBackButtonNavigationHandlers(final IPageWithHeader myView, final IEvent eventToFire) {
		if (myView.getHeader() != null) {
			ClickHandler backClickHandler = new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					PresenterController.get().beforePageLeave(new AsyncCallbackSuccess<Boolean>() {

						@Override
						public void onSuccess(Boolean result) {
							if (result) {
								beforeBack();
								Utils.console("History Back Event >" + eventToFire.eventName());
								dispatcher.fireMobileEvent(eventToFire, myView.getView());
							}
						}
					});

				}
			};
			myView.getHeader().setLeftButtonClickHandler(backClickHandler);
			return backClickHandler;
		}

		return null;


	}

	/**
	 * Created and adds a click handler for the back button. When the back button will be clicked thepassed callback
	 * will be executed.
	 *
	 * @param myView   currently displayed view.
	 * @param callback the callback executed after the back button will be clicked.
	 * @return the created internal click handler (e.g. to fire the event manually)
	 */
	protected ClickHandler addBackButtonNavigationHandlers(final IPageWithHeader myView,
														   final IBackCallback callback) {
		if (myView.getHeader() != null) {
			ClickHandler backClickHandler = new ClickHandler() {

				@Override
				public void onClick(final ClickEvent event) {
					PresenterController.get().beforePageLeave(new AsyncCallbackSuccess<Boolean>() {
						@Override
						public void onSuccess(Boolean result) {
							if (result) {
								beforeBack();
								callback.onBack(event);
							}
						}
					});

				}
			};
			myView.getHeader().setLeftButtonClickHandler(backClickHandler);
			return backClickHandler;
		}

		return null;


	}

	/**
	 * Will be called, before back action is executed.
	 */
	protected void beforeBack() {
		isVisible = false;
		nextTransitionDirection = Direction.LEFT;
	}


	/**
	 * Liefert rue wenn der vom Presenter kontollierte View sichtbar ist.
	 *
	 * @return true wenn sichtbar.
	 */
	@Override
	public boolean isOwningViewVisible() {
		return isVisible;
	}


	/**
	 * Generische trasitionsmethode fuer den Seitenwechsel.
	 *
	 * @param newRootContentArea new root content area. All new sceens will be places as chidlern of this area.
	 * @param startupCuntent     view which should be deplayed as content of the new root panel
	 */
	public void setRootContentArea(final IPage newRootContentArea, final IPageWithoutHeader startupCuntent) {
		// falls das Ursprungs-Panel das Main-View ist die content area wechseln
		if (globalContentArea == null && newRootContentArea != null && newRootContentArea instanceof IMainView) {
			globalContentArea = ((IMainView) newRootContentArea).toolbarPannel().getContentArea();
			globalContentArea.add(startupCuntent.getView().asComposite());
		}
	}

	/**
	 * Generic method to switch to another page. Shortcut for {code}gotoPage(originator, targetView, true, null, null){code}.
	 *
	 * @param originator the view which the event to switch the page originated from
	 * @param targetView view to switch to (usually the view of the current presenter)
	 */
	@Override
	public void gotoPage(final IPage originator, final IPageWithoutHeader targetView) {
		if (transitionsFromMainViewDisabled) {
			gotoPage(originator, targetView, !(originator instanceof IMainView), null, null);
		} else {
			gotoPage(originator, targetView, true, null, null);
		}
	}


	/**
	 * Generic method to switch to another page.
	 *
	 * @param originator          the view which the event to switch the page originated from
	 * @param targetView          view to switch to (usually the view of the current presenter)
	 * @param automaticTransition whether the transition effect should be determined automatically
	 *                            (parameters transition, transitionDirection are ignored)
	 * @param transition          transition effect ({code}null{code} for no transition effect)
	 * @param transitionDirection transition direction
	 */
	@Override
	public void gotoPage(final IPage originator, final IPageWithoutHeader targetView, boolean automaticTransition,
						 Transition transition, Direction transitionDirection) {
		isVisible = true;
		PresenterController.get().setActivePresenter(this, targetView.getView());
		//if a page is shown but next transisition event is used with wrong originator
		if (lastDisplayedPage != null && lastDisplayedPage.getView() != originator) {
			lastDisplayedPage.getView().beforeLeaving();
		}
		lastDisplayedPage = targetView;
		setRootContentArea(originator, targetView);
		if (globalContentArea != null) {
			targetView.getView().setParent(globalContentArea);
			originator.setParent(globalContentArea);
		}

		// keine transition wenn Main-Wiew
		if (!transitionIsRunning && originator != null) {
			transitionIsRunning = true;
			ITransitionCompletedCallback transitionCompletedCallback = new ITransitionCompletedCallback() {
				@Override
				public void isCompleted() {
					Utils.console("Transition to Page (" + targetView.getView().getName() + ") completed");
					transitionIsRunning = false;
				}
			};

			if (automaticTransition) {
				originator.goTo(targetView.getView(), getNextTransitionDirection(), transitionCompletedCallback);
			} else if (originator instanceof SimplePage) {
				// TODO: add goTo method with transition parameter to IPage? (currently not possible as IPage is part of swm-gwt-client but
				// Transition is part of swm-commons-mobile)
				((SimplePage) originator).goTo(targetView.getView(), transition, transitionDirection, transitionCompletedCallback);
			} else {
				originator.goTo(targetView.getView(), transitionDirection, transitionCompletedCallback);
			}
		} else if (!transitionIsRunning && originator == null) {
			// es gibt keinen originator
			Scheduler.get().scheduleDeferred(new ScheduledCommand() {
				@Override
				public void execute() {
					SimplePage.load(targetView.getView());
					transitionIsRunning = false;
				}
			});
			transitionIsRunning = true;
		} else {
			Utils.console("Cannot perform transition to page (" + targetView.getView().getName() + "), because other transition is still running.");
		}

	}

	/**
	 * Generische trasitionsmethode fuer den Seitenwechsel.
	 *
	 * @param targetView die view des aktuellen Presenters (Ziel) - als Content area des Ziels wird Root gesetzt.
	 */
	@Override
	public void gotoPageWithRootAsContentArea(final IPageWithoutHeader targetView) {
		PresenterController.get().setActivePresenter(this, targetView.getView());
		Scheduler.get().scheduleDeferred(new ScheduledCommand() {
			@Override
			public void execute() {
				SimplePage.load(targetView.getView());
				transitionIsRunning = false;
			}
		});
		transitionIsRunning = true;

	}

	/**
	 * Callback, when the back button is clicked.
	 */
	public static interface IBackCallback {
		void onBack(ClickEvent clickEvent);
	}
	
	/**
	 * Returns {code}true{code} if a transition is currently running (in this case, goToPage() has no effect).
	 * @return {code}true{code} if a transition is currently running
	 */
	protected static boolean getTransitionIsRunning() {
		return transitionIsRunning;
	}

}
